package sg.edu.nus.iss.vmcs.customer;

import sg.edu.nus.iss.vmcs.store.*;
import sg.edu.nus.iss.vmcs.system.MainController;
import sg.edu.nus.iss.vmcs.system.SimulatorControlPanel;
import sg.edu.nus.iss.vmcs.util.VMCSException;

/**
* This class represents the control object that coordinates the customer transactions 
* for selection of a drink brand, coin input, storage of coins and termination requests
* for ongoing transactions.
*
* @version 3.0 29/11/2011
* @author  Team 7S - SE19 
*/
public class TransactionController {

	private MainController     mainCtrl;
	private StoreController    storeCtrl;
	private CoinReceiver       coinReceive;
	private DispenseController dispenseCtrl;
	private CustomerPanel   cpanel;
	private ChangeGiver changeGiver; 
	
	private boolean changeGiven;
	private boolean drinkDispensed;
	private boolean cashStored;
	
	private int price;
	private int selection;
	private int totalInserted;
	
/**
 * This constructor creates an instance of the Transaction Controller
 * @param mctrl Main Controller
 * @see MainController
 */
	public TransactionController(MainController mctrl) {
		changeGiven = false;
		drinkDispensed = false;
		price = 0;
		selection = -1;
		
		mainCtrl  = mctrl;
		storeCtrl = mctrl.getStoreController();
		this.InitController();
	}

/**
 * This operation creates the new instances related to customer function control
 * objects such as {@link CoinReceiver}, {@link DispenseController}, {@link ChangeGiver}.
 */
	public void InitController() {
		coinReceive  = new CoinReceiver(this);
		dispenseCtrl = new DispenseController(this);
		changeGiver = new ChangeGiver(this);
	}

/**
 * This operation returns the reference for MainController object as return parameter.
 * @return MainController
 */	
	public MainController getMainController() {
		return mainCtrl;
	}

/**
 * This operation returns the reference for StoreController object as return parameter.
 * @return StoreController
 */
	public StoreController getStoreController() {
		return storeCtrl;
	}

/**
 * This operation returns the reference for CoinReceiver object as return parameter.
 * @return CoinReceiver
 */	
	public CoinReceiver getCoinReceiver() {
		return coinReceive;
	}

/**
 * This operation return CustomerPanel object reference as return parameter.
 * @return CustomerPanel
 */	
	public CustomerPanel getCustomerPanel() {
		return cpanel;
	}
	
/**
 * This operation enables to setup the customer panel and display it.
 */

	public void displayCustomerPanel() {
		SimulatorControlPanel scp = mainCtrl.getSimulatorControlPanel();
		if (cpanel == null)
			cpanel = new CustomerPanel(scp, this);
		cpanel.display();
		scp.setActive(SimulatorControlPanel.ACT_CUSTOMER, false);
	}

/**
 * This operation removes the customer panel and all its constituent objects.
 */	
	public void closeDown () {
		if (cpanel == null)
			return;
		cpanel.dispose();
		SimulatorControlPanel scp = mainCtrl.getSimulatorControlPanel();
		scp.setActive(SimulatorControlPanel.ACT_CUSTOMER, true);
	}
	
/**
 * This operation will start the customer transaction. It receives the identification for the selected drink brand (item) from the Customer Panel. The following actions are performed in the operation:
 * 1 The price of the selected item is obtained.
 * 2 The Refund/Change Tray Display is reset.
 * 3 The Can Collection Box is reset.
 * 4 The Drink Selection Box is deactivated to disallow the selection of further drinks when the transaction is in progress.
 * 5 The Coin Receiver will be instructed to start receiving the coins.
 * @param idertifier Integer Value
 */	
	public void startTransaction(int idertifier){
		
		//Get price
		DrinksStore drinksStore =(DrinksStore)storeCtrl.getStore(Store.DRINK);
		DrinksStoreItem drinksStoreItem = (DrinksStoreItem)drinksStore.getStoreItem(idertifier);
		DrinksBrand drinksBrand = (DrinksBrand)drinksStoreItem.getContent();
		this.price = drinksBrand.getPrice();
		this.selection = idertifier;
		
		changeGiver.resetChange();
		dispenseCtrl.resetCan();
		changeGiver.displayChangeStatus();
		dispenseCtrl.allowSelection(false);
		coinReceive.startReceive();
	}

/**
 * This operation processes the money received by the Coin Receiver during the progress of a transaction.
 * The following actions are performed during this operation:
 * 1 The current total money inserted is obtained from the Coin Receiver.
 * 2 If the received money is more than or equal to the price of the drink, operation 
 * CompleteTransaction of the Transaction Controller is triggered.
 * 3 If the received money is less than the price of the drink, the Coin Receiver
 * is instructed to continue receiving the coins. 
 * @param total Integer value
 * @throws VMCSException Exception on Error
 */	
	public void processMoneyReceived(int total) throws VMCSException{
		this.totalInserted = total;
		if(this.totalInserted >= this.price){
			completeTransaction();
		}else{
			this.coinReceive.continueReceive();
		}
	}

/**
 * This operation is performed when the Transaction Controller is informed that 
 * coin entry is complete and the money received is sufficient to dispense 
 * the drink. The following actions are performed.
 * 1 Dispense the drink.
 * 2 Give change if necessary.
 * 3 Store the Coins that have been entered into the Cash Store.
 * 4 Reset the Drink Selection Box to allow further transactions.
 * @throws VMCSException Exception Error
 */	
	private void completeTransaction() throws VMCSException{
		try{
			drinkDispensed = this.dispenseCtrl.dispenseDrink(selection);
			if(!drinkDispensed){
				terminateTransaction();
				return;
			}
				
			int changeRequired = this.totalInserted - this.price;
			if(changeRequired > 0){
				changeGiven = this.changeGiver.giveChange(changeRequired);
				if(!changeGiven){
					terminateTransaction();
					return;
				}
			}
			
			cashStored = this.coinReceive.storeCash();
			if(!cashStored){
				terminateTransaction();
				return;
			}
			
			this.dispenseCtrl.allowSelection(true);	
			
		}catch(VMCSException ex){
			terminateTransaction();
			throw ex;
		}
	}

/**
 * This operation will perform, if the Transaction Controller is informed 
 * that a fault was discovered while dispensing a drink, giving change or 
 * storing Coins, it will use this operation to deactivate the Drink Selection Box 
 * and instruct the Coin Receiver to refund the money inserted by the customer. 
 * @param Source Object
 * @throws VMCSException
 */	
	public void terminateFault(Object Source) throws VMCSException{
		this.dispenseCtrl.allowSelection(false);
		if(Source.getClass().getName().equalsIgnoreCase("DispenseController")){
			this.coinReceive.refundCash();
		}
		if(Source.getClass().getName().equalsIgnoreCase("ChangeGiver")){
			this.coinReceive.storeCash();
		}
		if(Source.getClass().getName().equalsIgnoreCase("CoinReceiver")){
			
		}
	}

/**
 * This operation will perform, if the Transaction Controller receives a request to terminate the current transaction 
 * then following will occur:
 * 1 If there is no transaction in progress or coin input is completed then the Customer Panel
 *   will be instructed to deactivate the Drink Selection Box.
 * 2 If coin input is not yet complete, the Coin Receiver will be 
 *   instructed to stop coin input and refund the money entered so far.
 * 3 The Drink Selection Box is then reset to allow further transactions.
 */	
	public void terminateTransaction(){
		//deactive drink selection
		this.dispenseCtrl.allowSelection(false);
		
		//stop coin entry
		this.coinReceive.stopReceive();
		
		//refund money so far entered
		this.coinReceive.refundCash();	
	}

/**
 * This operation will cancel an ongoing customer transaction. 
 */	
	public void cancelTransaction(){
		//stop coin entry
		this.coinReceive.stopReceive();
		
		//refund money so far entered
		this.coinReceive.refundCash();	
		
		//active drink selection
		this.dispenseCtrl.allowSelection(true);
	}
/**
 * This operation returns the reference for the DispenseController object
 * @return DispenseController
 */
	public DispenseController getDispenseCtrl() {
		return dispenseCtrl;
	}
}
